package lexer;

import com.wang.define.XmlChar;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Hashtable;

/**
 * User: bohao.wang
 * Date: 12-4-4
 * Time: 上午10:22
 */
public class Lexer {
    public int line = 1;

    // peek = 1表示需要读取下一位
    // 所以某些时候需要手动至1
    char peek = 1;
    Hashtable<String, Word> words = new Hashtable<String, Word>();
    String text;
    int ptr = 0;

    void reserve(Word w) {
        words.put(w.lexeme, w);
    }

    public Lexer(BufferedReader in) throws IOException {
        String t;
        StringBuilder str = new StringBuilder();
        while ((t = in.readLine()) != null) {
            str.append(t).append('\n');
        }
        text = str.toString();
    }

    boolean hasNext() {
        return text.length() > ptr;
    }

    void readch() {
        if(text.length() > ptr)
            peek = text.charAt(ptr);
        else peek = 0;
        ptr++;
    }

    boolean readch(String abc) {
        peek = 1;
        if(abc.length() + ptr < text.length()) {
            for(int i = 0; i < abc.length(); ++i) {
                if(text.charAt(ptr + i) != abc.charAt(i))
                    return false;
            }
            ptr += abc.length();
            if(abc.endsWith(" "))ptr--;
            return true;
        }

        return false;
    }

    // scan对于处理-的支持不是很好
    // 会读入包含-的词组，所以像处理 -->这样的符号时
    // 需要手动判断
    public Token scan() throws IOException {
        if( !hasNext() )throw new RuntimeException("EOF");
        if(peek == 1)readch();
        int sCount = 0;
        for (; ; readch()) {
            if (peek == ' ' || peek == '\t') {
                sCount++; continue;
            }
            else if (peek == '\n') {
                line = line + 1;
                sCount++;
            }
            else break;
        }
        if(sCount > 0) return new Word(" ",Tag.separator);
        switch (peek) {
            case '<':
                if(readch("?xml ")) return Word.xml;
                else if(readch("!--")) return Word.comment;
                else if(readch("![CDATA[")) return Word.cdstart;
                else if(readch("?")) return Word.pi;
                else if(readch("/")) return Word.end;
                else if(readch("!DOCTYPE"))
                    return new Word("<!DOCTYPE", Tag.doctype);
                else if(readch("!ELEMENT"))
                    return new Word("<!ELEMENT", Tag.elementdecl);
                else if(readch("!ATTLIST"))
                    return new Word("<!ATTLIST", Tag.attlistdecl);
                else if(readch("!ENTITY"))
                    return new Word("<!ENTITY", Tag.entitydecl);
                else if(readch("!NOTATION"))
                    return new Word("<!NOTATION", Tag.notationdecl);
                else return new Token('<');
            case '?':
                if(readch(">")) return Word.qend;
                else return new Token('?');
            case '=':
                peek = 1;
                return new Token('=');
            case '-':
                if(readch("->")) return Word.commentEnd;
                else return new Token('-');
            case '/':
                if(readch(">")) return Word.emptyEnd;
                else return new Token('/');
            case ']':
                if(readch("]>")) return Word.cdend;
                else return new Token(']');
        }
        if( Character.isLetterOrDigit(peek) || peek == '.' || peek == '_') {
            StringBuilder b = new StringBuilder();
            do {
                b.append(peek); readch();
            } while(XmlChar.isNameChar(peek));
            String s = b.toString();
            Word w = words.get(s);
            if(w != null) return w;
            w = new Word(s, Tag.id);
            words.put(s, w);
            return w;
        }
        Token tok = new Token(peek);
        peek = 1;
        return tok;
    }

}
